package demo;

import cn.hutool.core.date.DateUtil;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Objects;

/**
 * 对象差异比较工具类
 * 用于对比两个对象的差异并生成差异信息
 * 需要配合@ValueComparison注解使用
 *
 * @author feixiang.li
 * @since 2024-10-15 15:56
 */
@Slf4j
public final class CompareUtils {

    /**
     * 枚举类必须存在的翻译方法名
     */
    private static final String TRANSLATE_METHOD = "getNameByIndex";
    /**
     * 空值显示文本
     */
    private static final String NULL_STRING = "空";
    /**
     * 字段分隔符
     */
    private static final String SEMICOLON = "；";
    /**
     * 字段名和值分隔符
     */
    private static final String COLON = "：";
    /**
     * 新旧值分隔符
     */
    private static final String ARROW = ">>";
    /**
     * 错误提示信息
     */
    private static final String ERROR_MSG = "更新日志生成错误";

    private CompareUtils() {
    }

    /**
     * 对比两个对象的差异并生成差异信息
     *
     * @param oldObj                原始对象
     * @param newObj                新对象
     * @param ignoreNewObjNullValue 是否忽略新对象中的空值字段
     * @return 格式化后的差异信息字符串
     * @throws RuntimeException 如果比较过程中出现错误
     */
    public static String compareUpdateInfo(Object oldObj, Object newObj, boolean ignoreNewObjNullValue) {
        // 参数校验
        Objects.requireNonNull(oldObj, "原始对象不能为空");
        Objects.requireNonNull(newObj, "新对象不能为空");

        // 类型校验
        if (!oldObj.getClass().equals(newObj.getClass())) {
            log.error("要比较的两个对象类型不一致");
            throw new RuntimeException(ERROR_MSG);
        }

        StringBuilder content = new StringBuilder();
        Field[] fields = FieldUtils.getAllFields(oldObj.getClass());

        // 遍历所有字段
        for (Field field : fields) {
            // 只处理带有@ValueComparison注解的字段
            if (!field.isAnnotationPresent(ValueComparison.class)) {
                continue;
            }

            try {
                // 处理单个字段的比较
                processField(field, oldObj, newObj, ignoreNewObjNullValue, content);
            } catch (IllegalAccessException e) {
                log.error("字段访问异常", e);
                throw new RuntimeException(ERROR_MSG, e);
            }
        }

        return content.toString();
    }

    /**
     * 处理单个字段的比较逻辑
     */
    private static void processField(Field field, Object oldObj, Object newObj,
                                     boolean ignoreNewObjNullValue, StringBuilder content)
            throws IllegalAccessException {
        // 设置字段可访问
        field.setAccessible(true);
        ValueComparison annotation = field.getAnnotation(ValueComparison.class);

        // 获取新值
        Object newValue = field.get(newObj);
        // 如果忽略空值且新值为空，则跳过
        if (ignoreNewObjNullValue && newValue == null) {
            return;
        }

        // 获取旧值
        Object oldValue = field.get(oldObj);
        // 如果值相同，则跳过
        if (Objects.equals(oldValue, newValue)) {
            return;
        }

        // 添加字段名
        content.append(annotation.fieldName()).append(COLON);

        // 根据字段类型处理值
        if (annotation.enumClazz().isEnum()) {
            // 处理枚举类型字段
            appendEnumValue(content, oldValue, newValue, annotation);
        } else {
            // 处理普通类型字段
            appendRegularValue(content, oldValue, newValue, annotation);
        }

        // 添加字段分隔符
        content.append(SEMICOLON);
    }

    /**
     * 处理枚举类型字段的值
     */
    private static void appendEnumValue(StringBuilder content, Object oldValue, Object newValue,
                                        ValueComparison annotation) {
        try {
            // 获取枚举翻译方法
            Method method = annotation.enumClazz().getMethod(TRANSLATE_METHOD, Integer.class);
            // 添加旧值和新值
            content.append(getFormattedValue(oldValue, method, annotation))
                    .append(ARROW)
                    .append(getFormattedValue(newValue, method, annotation));
        } catch (NoSuchMethodException e) {
            log.error("找不到枚举翻译方法", e);
            throw new RuntimeException(ERROR_MSG, e);
        }
    }

    /**
     * 处理普通类型字段的值
     */
    private static void appendRegularValue(StringBuilder content, Object oldValue, Object newValue,
                                           ValueComparison annotation) {
        // 格式化并添加旧值和新值
        content.append(formatValue(oldValue, annotation.datePattern()))
                .append(ARROW)
                .append(formatValue(newValue, annotation.datePattern()));
    }

    /**
     * 获取格式化后的枚举值
     */
    private static Object getFormattedValue(Object value, Method method, ValueComparison annotation) {
        return value == null ? NULL_STRING : invokeMethod(value, method, annotation);
    }

    /**
     * 格式化字段值
     */
    private static String formatValue(Object value, String datePattern) {
        if (value == null) {
            return NULL_STRING;
        }
        // 如果是日期类型，按指定格式格式化
        if (value instanceof Date) {
            return DateUtil.format((Date) value, datePattern);
        }
        return value.toString();
    }

    /**
     * 调用枚举翻译方法
     */
    private static Object invokeMethod(Object obj, Method method, ValueComparison annotation) {
        try {
            // 处理Byte类型，转换为int
            if (obj instanceof Byte) {
                Integer objInt = ((Byte) obj).intValue();
                return method.invoke(annotation.enumClazz(),objInt);
            }
            return method.invoke(annotation.enumClazz(), obj);
        } catch (Exception e) {
            log.error("调用枚举翻译方法异常", e);
            throw new RuntimeException("枚举值处理失败", e);
        }
    }
}